package com.feonix.common.datasource;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.logicalcobwebs.proxool.ProxoolDataSource;

import com.feonix.common.BaseInfo;
import com.feonix.common.util.FileUtil;
import com.feonix.common.util.Properties;
import com.feonix.common.util.StringUtil;
import com.feonix.common.validation.ValidationUtil;

import net.sf.oval.constraint.NotBlank;
import net.sf.oval.constraint.NotNull;

public class DataSourceConfig {

	final Log logger = LogFactory.getLog(super.getClass());
	@NotNull
	@NotBlank
	private String alias;

	@NotNull
	@NotBlank
	private String ip;

	@NotNull
	@NotBlank
	private String port;

	@NotNull
	@NotBlank
	private String dbName;

	@NotNull
	@NotBlank
	private String dbUser;

	@NotNull
	@NotBlank
	private String dbPassword;

	@NotNull
	@NotBlank
	private String dbType;

	private boolean testBeforeUse = false;

	private boolean testAfterUse = false;

	private int minimumConnectionCount = 10;

	private int maximumConnectionCount = 30;
	private String dbDriver;
	private String url;
	private String validationQuery;
	private String charset;
	private boolean trace = false;

	private int prototypeCount = 5;

	private int simultaneousBuildThrottle = 10;

	public DataSourceConfig() {

	}

	public DataSourceConfig(String alias, String ip, String port, String dbName, String dbUser, String dbPassword,
			String dbType) {
		this.alias = alias;
		this.ip = ip;
		this.port = port;
		this.dbName = dbName;
		this.dbUser = dbUser;
		this.dbPassword = dbPassword;
		this.dbType = dbType;
		config();
	}

	public DataSourceConfig(String alias, String ip, String port, String dbName, String dbUser, String dbPassword,
			String dbType, String charset) {
		this.alias = alias;
		this.ip = ip;
		this.port = port;
		this.dbName = dbName;
		this.dbUser = dbUser;
		this.dbPassword = dbPassword;
		this.dbType = dbType;
		this.charset = charset;
		config();
	}

	public static List<DataSourceConfig> getCustomDataSourceConfigs() {
		ArrayList<DataSourceConfig> dataSourceConfigList = null;
		File dbPropertiesPath = new File(BaseInfo.getClassPath() + File.separator + "extend_db");
		if (dbPropertiesPath.exists()) {
			List<File> fileList = FileUtil.filterFiles(dbPropertiesPath, new String[] { "properties" }, true);
			if (CollectionUtils.isNotEmpty(fileList)) {
				dataSourceConfigList = new ArrayList<DataSourceConfig>();
				for (File file : fileList) {
					DataSourceConfig dataSourceConfig = getDataSourceConfig(file.getAbsolutePath(), null);
					dataSourceConfigList.add(dataSourceConfig);
				}
			}
		}
		return dataSourceConfigList;
	}

	public DataSourceConfig(String alias, String ip, String port, String dbName, String dbUser, String dbPassword,
			String dbType, String charset, String url) {
		this.alias = alias;
		this.ip = ip;
		this.port = port;
		this.dbName = dbName;
		this.dbUser = dbUser;
		this.dbPassword = dbPassword;
		this.dbType = dbType;
		this.charset = charset;
		this.url = url;
		config();
	}

	public static DataSourceConfig getDefaultDataSourceConfig() {
		// 结尾带“/”
		String filePath = DataSourceConfig.class.getClassLoader().getResource("db.properties").getPath();
		return getDataSourceConfig(filePath, "default");
	}

	/**
	 * 载入数据库连接
	 * 
	 * @param filePath
	 * @param defAlias
	 * @return
	 */
	private static DataSourceConfig getDataSourceConfig(String filePath, String defAlias) {
		Properties properties = new Properties(filePath);
		String alias = properties.getString("jdbc.alias", defAlias);
		String url = properties.getString("jdbc.url");
		String ip = properties.getString("jdbc.ip");
		String port = properties.getString("jdbc.port");
		String dbname = properties.getString("jdbc.dbname");
		String username = properties.getString("jdbc.username");
		String password = properties.getString("jdbc.password");
		String dbtype = properties.getString("jdbc.dbtype");
		String charset = properties.getString("jdbc.charset");
		String validationQuery = properties.getString("jdbc.validationQuery");
		Boolean trace = properties.getBoolean("jdbc.trace");
		int maximumConnectionCount = properties.getInt("jdbc.maximumConnectionCount", 30);
		int minimumConnectionCount = properties.getInt("jdbc.minimumConnectionCount", 10);
		int prototypeCount = properties.getInt("jdbc.prototypeCount", 5);
		int simultaneousBuildThrottle = properties.getInt("jdbc.simultaneousBuildThrottle", 10);
		Boolean testBeforeUse = properties.getBoolean("jdbc.testBeforeUse");
		Boolean testAfterUse = properties.getBoolean("jdbc.testAfterUse");

		DataSourceConfig dataSourceConfig = new DataSourceConfig(alias, ip, port, dbname, username, password, dbtype,
				charset);
		if (StringUtil.isNotEmpty(url)) {
			dataSourceConfig.setUrl(url);
		}
		dataSourceConfig.setMaximumConnectionCount(maximumConnectionCount);
		dataSourceConfig.setMinimumConnectionCount(minimumConnectionCount);
		dataSourceConfig.setPrototypeCount(prototypeCount);
		dataSourceConfig.setSimultaneousBuildThrottle(simultaneousBuildThrottle);
		if (trace != null) {
			dataSourceConfig.setTrace(trace);
		}
		if (StringUtil.isNotEmpty(validationQuery)) {
			dataSourceConfig.setValidationQuery(validationQuery);
		}
		if (testBeforeUse != null) {
			dataSourceConfig.setTestBeforeUse(testBeforeUse.booleanValue());
		}
		if (testAfterUse != null) {
			dataSourceConfig.setTestAfterUse(testAfterUse.booleanValue());
		}
		return dataSourceConfig;
	}

	public void config() {
		int dbType = NumberUtils.toInt(this.dbType);
		if (dbType == 1) { // oracle
			this.dbDriver = "oracle.jdbc.driver.OracleDriver";
			if (!(StringUtil.contains(StringUtil.getString(this.url).toUpperCase(), "CONNECT_DATA"))) {
				this.url = "jdbc:oracle:thin:@" + this.ip + ":" + this.port + ":" + this.dbName;
			}
			this.validationQuery = "select 1 from dual";
		} else if (dbType == 2) { // mssql
			if (StringUtil.isEmpty(this.charset)) {
				this.charset = "UTF-8";
			}
			this.dbDriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
			this.url = "jdbc:sqlserver://" + this.ip + ":" + this.port + ";DatabaseName=" + this.dbName + ";charset="
					+ this.charset + ";SendStringParametersAsUnicode=false";
			this.validationQuery = "select 'x'";
		} else if (dbType == 3) { // db2
			this.dbDriver = "com.ibm.db2.jcc.DB2Driver";
			this.url = "jdbc:db2://" + this.ip + ":" + this.port + "/" + this.dbName;
			this.validationQuery = "";
		} else if (dbType == 5) { // mysql
			if (StringUtil.isEmpty(this.charset)) {
				this.charset = "utf8";
			}
			this.dbDriver = "com.mysql.jdbc.Driver";
			this.url = "jdbc:mysql://" + this.ip + ":" + this.port + "/" + this.dbName + "?characterEncoding="
					+ this.charset + "&autoReconnect=true";
			this.validationQuery = "select 1 from dual";
		}
		if (this.maximumConnectionCount == 0) {
			this.maximumConnectionCount = 30;
		}
		if (this.minimumConnectionCount == 0) {
			this.minimumConnectionCount = 10;
		}
		if (this.prototypeCount == 0) {
			this.prototypeCount = 5;
		}
		if (this.simultaneousBuildThrottle == 0) {
			this.simultaneousBuildThrottle = 10;
		}
	}

	public ProxoolDataSource getDataSource() {
		ProxoolDataSource dataSource = null;
		try {
			ValidationUtil.validation(this);
			dataSource = new ProxoolDataSource(this.alias);
			dataSource.setDriver(this.dbDriver);
			dataSource.setDriverUrl(this.url);
			dataSource.setUser(this.dbUser);
			dataSource.setPassword(this.dbPassword);
			dataSource.setHouseKeepingTestSql(this.validationQuery);
			dataSource.setStatisticsLogLevel("error");
			dataSource.setHouseKeepingSleepTime(30000);
			dataSource.setMaximumConnectionLifetime(14400000);
			dataSource.setRecentlyStartedThreshold(60000);
			dataSource.setOverloadWithoutRefusalLifetime(60000);
			dataSource.setSimultaneousBuildThrottle(this.simultaneousBuildThrottle);
			dataSource.setPrototypeCount(this.prototypeCount);
			dataSource.setTestBeforeUse(this.testBeforeUse);
			dataSource.setTestAfterUse(this.testAfterUse);
			dataSource.setMaximumConnectionCount(this.maximumConnectionCount);
			dataSource.setMinimumConnectionCount(this.minimumConnectionCount);
			dataSource.setMaximumActiveTime(180000L);
			dataSource.setTrace(this.trace);
		} catch (Exception e) {
			logger.warn(e.getMessage());
		}
		return dataSource;
	}

	public String getAlias() {
		return alias;
	}

	public void setAlias(String alias) {
		this.alias = alias;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}

	public String getDbName() {
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
	}

	public String getDbUser() {
		return dbUser;
	}

	public void setDbUser(String dbUser) {
		this.dbUser = dbUser;
	}

	public String getDbPassword() {
		return dbPassword;
	}

	public void setDbPassword(String dbPassword) {
		this.dbPassword = dbPassword;
	}

	public String getDbType() {
		return dbType;
	}

	public void setDbType(String dbType) {
		this.dbType = dbType;
	}

	public boolean isTestBeforeUse() {
		return testBeforeUse;
	}

	public void setTestBeforeUse(boolean testBeforeUse) {
		this.testBeforeUse = testBeforeUse;
	}

	public boolean isTestAfterUse() {
		return testAfterUse;
	}

	public void setTestAfterUse(boolean testAfterUse) {
		this.testAfterUse = testAfterUse;
	}

	public int getMinimumConnectionCount() {
		return minimumConnectionCount;
	}

	public void setMinimumConnectionCount(int minimumConnectionCount) {
		this.minimumConnectionCount = minimumConnectionCount;
	}

	public int getMaximumConnectionCount() {
		return maximumConnectionCount;
	}

	public void setMaximumConnectionCount(int maximumConnectionCount) {
		this.maximumConnectionCount = maximumConnectionCount;
	}

	public boolean isTrace() {
		return trace;
	}

	public void setTrace(boolean trace) {
		this.trace = trace;
	}

	public int getPrototypeCount() {
		return prototypeCount;
	}

	public void setPrototypeCount(int prototypeCount) {
		this.prototypeCount = prototypeCount;
	}

	public int getSimultaneousBuildThrottle() {
		return simultaneousBuildThrottle;
	}

	public void setSimultaneousBuildThrottle(int simultaneousBuildThrottle) {
		this.simultaneousBuildThrottle = simultaneousBuildThrottle;
	}

	public String getDbDriver() {
		return dbDriver;
	}

	public void setDbDriver(String dbDriver) {
		this.dbDriver = dbDriver;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getValidationQuery() {
		return validationQuery;
	}

	public void setValidationQuery(String validationQuery) {
		this.validationQuery = validationQuery;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

}
